#include "common.h"


long serverCron(Server *server, long id, void *clientData) 
{
    /* Callback when time expired, the return value indicates the next expired time */
    Log_Write_Stderr("Now %d Conn Left", g_server->free_connection_n);
    return 60000;
}

void EVENT_Accept(Event *ev)
{
    int clientfd;
    Conn *c = ev->data;
    ds_pool_s *pool;

    ev->ready = 0;
    clientfd = NET_TcpAccept(g_server->serverfd);
    if (clientfd == -1) {
        /* 
                Thundering herd problems:
                In multi-process, if you watch the same listening socket in different processes, when the event comes, 
                not only one process will be awaked, so it can be accepted if accept() return -1 and errno is EAGAIN.

                Todo : Add the accept mutex
                */

        return;
    }

    NET_NonBlock(clientfd);
    c = CONN_Get_Connection(clientfd);
    if (c == NULL) {
        Log_Write(g_server->log, "No Enough Conns");
        return;
    }
    c->read->handler = REQ_ProcessRequestLine;
    c->pool = DS_CreatePool(CONN_POOL_SIZE);
    if (c->pool == NULL) {
        CONN_Close_Connection(c);
        Log_Write(g_server->log, "No Enough Memory");
        return;
    }
    c->buffer = DS_CreateTempBuf(c->pool, MAX_REQUEST_BUF_LEN);
    if (c->buffer == NULL) {
        FinalizeConn(c);
        Log_Write(g_server->log, "Alloc temp buffer Failed");
        return;
    }
    if (EVENT_Handle_Read_Event(c->read, EVENT_CLEAR_EVENT) != EVENT_OK) {
        FinalizeConn(c);
        Log_Write(g_server->log, "Add Epoll ReadEvent Failed");
        return;
    }
    if (REQ_CreateRequest(c) != HTTP_OK) {
        FinalizeConn(c);
        Log_Write(g_server->log, "Create Request Failed");
        return;
    }
    if (RES_CreateResponse(c) != DS_OK) {
        FinalizeConn(c);
        Log_Write(g_server->log, "Create Response failed");
        return;
    }
    
}

void GetTime(long *seconds, long *milliseconds)
{
    struct timeval tv;

    gettimeofday(&tv, NULL);
    *seconds = tv.tv_sec;
    *milliseconds = tv.tv_usec/1000;
}

void AddMillisecondsToNow(long milliseconds, long *sec, long *ms) 
{
    long cur_sec, cur_ms, when_sec, when_ms;

    GetTime(&cur_sec, &cur_ms);
    when_sec = cur_sec + milliseconds/1000;
    when_ms = cur_ms + milliseconds%1000;
    if (when_ms >= 1000) {
        when_sec ++;
        when_ms -= 1000;
    }
    *sec = when_sec;
    *ms = when_ms;
}


/* Search the first event that will expire from timer event list. */
TimeEvent *SearchNearestTimer(Server *server)
{
    TimeEvent *te = server->timeEventHead;
    TimeEvent *nearest = NULL;

    while(te) {
        if (!nearest || te->when_sec < nearest->when_sec ||
                (te->when_sec == nearest->when_sec &&
                 te->when_ms < nearest->when_ms))
            nearest = te;
        te = te->next;
    }
    return nearest;
}

long EVENT_CreateTimeEvent(Server *server, long milliseconds, time_event_handler_pt proc, void *clientData)
{
    long id = server->timeEventNextId++;
    TimeEvent *te;

    /* we will not use the connection pool because timer event not belongs any connection, it is a cron */
    te = malloc(sizeof(*te));
    if (te == NULL) {
        Log_Write(server->log, "malloc failed");
        return EVENT_ERROR;
    }
    te->id = id;
    AddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
    te->timeProc = proc;
    te->clientData = clientData;
    te->next = server->timeEventHead;
    server->timeEventHead = te;
    return id;
}

int EVENT_DeleteTimeEvent(Server *server, long id)
{
    TimeEvent *te, *prev = NULL;

    te = server->timeEventHead;
    while(te) {
        if (te->id == id) {
            if (prev == NULL)
                server->timeEventHead = te->next;
            else
                prev->next = te->next;
            free(te);
            return EVENT_OK;
        }
        prev = te;
        te = te->next;
    }
    Log_Write(server->log, "Delete Time Event Failed");
    return EVENT_ERROR; 
}

int EVENT_Init(Server *server)
{
    Event *rev, *wev;
    int i;
    Conn *c, *next;
    
    server->epfd = epoll_create(MAX_CONN / 2);
    if (server->epfd == -1) {
        Log_Write(g_server->log, "Create Epoll Err");
        return EVENT_ERROR;
    } 

    server->timeEventHead = NULL;
    server->timeEventNextId  = 0;

    server->connections = DS_Pcalloc(server->pool, sizeof(Conn) * MAX_CONN);
    if (server->connections == NULL) {
        Log_Write(server->log, "Alloc Connections Err");
        return EVENT_ERROR;
    }
    server->read_events = DS_Pcalloc(server->pool, sizeof(Event) * MAX_CONN);
    if (server->read_events == NULL) {
        Log_Write(server->log, "Alloc Read Evnets Err");
        return EVENT_ERROR;
    }
    rev = server->read_events;
    for (i = 0; i < MAX_CONN; i++) {
        rev[i].closed = 1;
        rev[i].instance = 1;
    }
    server->write_events = DS_Pcalloc(server->pool, sizeof(Event) * MAX_CONN);
    if (server->write_events == NULL) {
        Log_Write(server->log, "Alloc Write Events Err");
        return EVENT_ERROR;
    }
    wev = server->write_events;
    for (i = 0; i < MAX_CONN; i++) {
        wev[i].closed = 1;
    }

    i = MAX_CONN;
    next = NULL;

    c = server->connections;

    do {
        i--;
	    c[i].index = i;
        c[i].data = next;
        c[i].read = &server->read_events[i];
        c[i].write = &server->write_events[i];
        c[i].fd =  -1;

        next = &c[i];

    } while (i);

    server->free_connections = next;
    server->free_connection_n = MAX_CONN;

    c = CONN_Get_Connection(server->serverfd);
    if (c == NULL) {
        Log_Write(server->log, "No Enough Connection");
        return EVENT_ERROR;
    }
    
    c->read->accept = 1;
    c->read->handler = EVENT_Accept;

    if (EVENT_Handle_Read_Event(c->read, 0) != EVENT_OK) {
        Log_Write(server->log, "Epoll Add Epoll Event Err");
        return EVENT_ERROR;
    }

    EVENT_CreateTimeEvent(server, 10000, serverCron, NULL);

    return EVENT_OK;
}

int EVENT_ProcessTimeEvents(Server* server) 
{
    TimeEvent *te;
    long maxId;

    te = server->timeEventHead;
    maxId = server->timeEventNextId - 1;
    
    while(te) {
        long now_sec, now_ms;
        long id;

        if (te->id > maxId) {
            te = te->next;
            continue;
        }
        GetTime(&now_sec, &now_ms);
        if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms))
        {
            int retval;

            id = te->id;
            retval = te->timeProc(server, id, te->clientData);
            if (retval != -1) {
                AddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
            } else {
                EVENT_DeleteTimeEvent(server, id);
            }
            te = server->timeEventHead;
        } else {
            te = te->next;
        }
    }
    return EVENT_OK;
}


int EVENT_Process_Events(int flags)
{
    int rc;
    int nevent;
    uint32_t    revents;
    int err;
    long timer;
    int i, j, instance;
    Conn  *c;
    Event *rev, *wev;
    TimeEvent *shortest = NULL;
    struct epoll_event events[EPOLL_EVENT];
    struct timeval tv, *tvp = NULL;

    if (!(flags & EVENT_TIME_EVENTS) && !(flags & EVENT_FILE_EVENT)) {
        Log_Write(g_server->log, "Need File Or(And) Timer Event");
        return EVENT_ERROR;
    }

    /* find the first expire element  and add the time into epoll */
    if ((flags & EVENT_TIME_EVENTS) && !(flags & EVENT_NOWAIT)) {
        
        shortest = SearchNearestTimer(g_server);
        if (shortest) {
            long now_sec, now_ms;
            
            GetTime(&now_sec, &now_ms);
            tvp = &tv;
            tvp->tv_sec = shortest->when_sec - now_sec;
            
            if (shortest->when_ms < now_ms) {
                tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;
                tvp->tv_sec --;
            } else {
                tvp->tv_usec = (shortest->when_ms - now_ms)*1000;
            }
            if (tvp->tv_sec < 0) {
                tvp->tv_sec = 0;
            }
            if (tvp->tv_usec < 0) {
                tvp->tv_usec = 0;
            }
        } else {
            if (flags & EVENT_NOWAIT) {
                tv.tv_sec = tv.tv_usec = 0;
                tvp = &tv;
            } else {
                tvp = NULL; 
            }
        }
    }

    timer = tvp? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1;
 
    nevent = epoll_wait(g_server->epfd, events, EPOLL_EVENT, timer);
    err = (nevent == -1) ? errno : 0;
    
    if (err) {
	    Log_Write(g_server->log, "Epoll wait Err %d", err);
        rc = EVENT_ERROR;
        goto Timer;
    }

    if (nevent == 0) {
        if (timer != -1) {
            rc = EVENT_OK;
        } else {
            rc = EVENT_ERROR;
        }
        
        goto Timer;
    }

    for (i = 0; i < nevent; i++) {
        c = events[i].data.ptr;
        instance = (uintptr_t) c & 1;
        c = (Conn *) ((uintptr_t) c & (uintptr_t) ~1);
        rev = c->read;
        if (c->fd == -1 || rev->instance != instance) {
    	    if (c->fd == -1) {
                //Log_Write(g_server->log, "Epoll Process Conn FD:%d index:%d", c->fd,c->index);
            } else {
                Log_Write(g_server->log, "stale event");
            }
	        continue;
        }
        
        revents = events[i].events;
        
        if (revents & (EPOLLERR|EPOLLHUP)) {
            Log_Write(g_server->log,"EpollERR: conn index is %d", c->index);
	    }
        
        if ((revents & (EPOLLERR|EPOLLHUP))
             && (revents & (EPOLLIN|EPOLLOUT)) == 0)
        {
            revents |= EPOLLIN|EPOLLOUT;
            Log_Write(g_server->log, "EPOLLERR | EPOLLHUP | MORE");
        }

        if ((revents & EPOLLIN) && rev->active) {
            rev->ready = 1;
            rev->handler(rev);
        }

        wev = c->write;
        if ((revents & EPOLLOUT) && wev->active) {
            wev->ready = 1;
            wev->handler(wev);
        }
    }

Timer:
    if (flags & EVENT_TIME_EVENTS)
        EVENT_ProcessTimeEvents(g_server);

    return EVENT_OK;    
}

int EVENT_Handle_Add_Connection(Conn *c)
{
    struct epoll_event  ee;

    ee.events = EPOLLIN|EPOLLOUT|EPOLLET;
    ee.data.ptr = (void *) ((uintptr_t) c | c->read->instance);

    if (epoll_ctl(g_server->epfd, EPOLL_CTL_ADD, c->fd, &ee) == -1) {
        return EVENT_ERROR;
    }

    c->read->active = 1;
    c->write->active = 1;

    return EVENT_OK;
    
}

int EVENT_Handle_Read_Event(Event *ev, int flags)
{
    int                  op;
    uint32_t             events, prev;
    Event   *e;
    Conn    *c;
    struct epoll_event   ee;

    if (ev->active || ev->ready) {
        return EVENT_OK;
    }
    
    c = ev->data;
    e = c->write;
    prev = EPOLLOUT;
    events = EVENT_READ_EVENT;
    if (e->active) {
        op = EPOLL_CTL_MOD;
        events |= prev;

    } else {
        op = EPOLL_CTL_ADD;
    }

    ee.events = events | flags;
    ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);

    if (epoll_ctl(g_server->epfd, op, c->fd, &ee) == -1) {
        Log_Write(g_server->log, "Add into epoll failed");
        return EVENT_ERROR;
    }

    ev->active = 1;

    return EVENT_OK;
    
}

int EVENT_Handle_Write_Event(Event *ev, int flag)
{
    int                  op;
    uint32_t             events, prev;
    Event   *e;
    Conn    *c;
    struct epoll_event   ee;
    
    if (ev->active || ev->ready) {
        return EVENT_OK;
    }

    c = ev->data;
    e = c->read;
    prev = EPOLLIN;
    events = EVENT_WRITE_EVENT;

    if (e->active) {
        op = EPOLL_CTL_MOD;
        events |= prev;

    } else {
        op = EPOLL_CTL_ADD;
    }

    ee.events = events | EVENT_CLEAR_EVENT;
    ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);

    if (epoll_ctl(g_server->epfd, op, c->fd, &ee) == -1) {
        Log_Write(g_server->log, "Add into epoll failed");
        return EVENT_ERROR;
    }

    ev->active = 1;

    return EVENT_OK;            
}

